package net.sf.fmj.media;

import java.util.logging.Logger;

import net.sf.fmj.utility.LoggerSingleton;

/**
 * Helps calculate and sleep based on buffer timestamps; useful for rendering
 * media at the correct rate. TODO: it is strange that we use this in the
 * RendererNode (which calls the Renderer), and in the RTPSyncBufferMux (but not
 * in the MuxNode). This implies that Multiplexers are responsible for timing
 * but not Renderers. This is perhaps a reasonable assumption, but needs to be
 * checked against JMF.
 * 
 * @author Ken Larson
 * 
 */
public class SleepHelper {
	private static final Logger logger = LoggerSingleton.logger;

	private long mStart;
	private long tbStart;
	private boolean firstNonDiscard = true;

	/**
	 * The number of nanoseconds in a millisecond.
	 */
	public static long MILLI_TO_NANO = 1000000L;

	public void reset() {
		mStart = 0;
		tbStart = 0;
		firstNonDiscard = true;
	}

	public void sleep(long currentTimestamp) throws InterruptedException {
		final long sleep = calculateSleep(currentTimestamp);
		if (sleep > 0) {
			logger.fine("Sleeping " + sleep);
			// Sleep until the next frame
			// TODO: the way sleeping should really work is that we start
			// processing the next frame while sleeping on the current one
			// (double buffer the filter graph)
			// TODO: where does JMF sleep? It does not appear that the renderer
			// does any sleeping.
			Thread.sleep(sleep);
			// TODO: I think the correct way to do this is to check the time
			// when we wake up and re-sleep if awoken prematurely.

		}

	}

	public long calculateSleep(long currentTimestamp) {
		final long tbNow = System.currentTimeMillis();

		// we don't want the first timestamp to be from a discarded frame.
		if (firstNonDiscard) {
			mStart = currentTimestamp;
			tbStart = tbNow;
			firstNonDiscard = false;
			return 0;
		}
		final long bufferTimeStamp = currentTimestamp;
		final float rate = 1; // TODO: support rates (controller/player rate)

		final long mTarget = bufferTimeStamp; // TODO: negative rates?
		// logger.fine("bufferTimeStamp=" + bufferTimeStamp + " tbNow=" +
		// tbNow);
		final long tbTarget = (long) ((double) (mTarget - mStart) / ((double) rate * (double) MILLI_TO_NANO))
				+ tbStart;
		final long sleep = tbTarget - tbNow;
		return sleep;

	}
}
